MODULE PARSE_CSV
! Programmed on Jun 10th 2013 by franco mele.
! 
! LIMITATIONS:
!
!   I suppose here to read a csv file as output from a sql query.
!   Undefined variables are declared as "(NULL)". I check for a "(" at
!   the beginning of a string to recognise the null variables. 
!   As a consequence, no (character) value can begin with "(".
!
! this module is used to parse a KNOWN string that contains 
! comma separated etherogeneous values (as from a csv file).
! It reads the nex value, but we have to KNOW the data-type in advance.
!
! Occorre sapere che tipo di variabile viene letta. Anzi, e' proprio il
! typo di argomento di i (variabile letta) che permette di chiamare la routine giusta.
!
! The overloading of the output variable permits to call the generic name.
! S  :  stringa di input completa
! i1,i2 : in ingresso , sono i limiti di ricerca del valore,
!         i1 deve essere definito alla prima chiamata (in genere = 1)
!         i1 in uscita indica il prossimo carattere utile (per la prossima variabile)
!         i2 indica l'ultimo carattere letto.
! l : lunghezza totale della stringa.
! i : variabile generica in uscita.
! n : in caso i sia character, indica la lunghezza della stringa di uscita.

   INTERFACE parse_csv_var     
     module procedure  parse_csv_real
     module procedure  parse_csv_int
     module procedure  parse_csv_bigint
     module procedure  parse_csv_double
     module procedure  parse_csv_char
   END INTERFACE parse_csv_var

CONTAINS



   subroutine parse_csv_int(sep,ier,s,i1,i2,l,i)
     integer, intent(IN) :: l
     integer, intent (INOUT) :: i1,i2
     character*(*), intent(IN) :: s
     character*1, intent(IN) :: sep
     integer, intent(OUT) :: i,ier
     integer add,ios
     
! suppose success
     ier=0
     i=0  ! zero the output
     if(s(i1:i1) .eq. sep .or. s(i1:i1) .eq. '(' )then
! the variable is undefined (I find a separator in first position or
!                            the beginning of the "(NULL)" statement):
        if(s(i1:i1) .eq. sep )then 
          ier=1    ! declare undefined variable
          i1=i1+1  ! bump the next good charater (next variable)
        else if(s(i1:i1+5) .eq. '(NULL)' .or.  s(i1:i1+5) .eq. '(null)')then
          ier=2    ! declare NULL variable
          i1=i1+7  ! bump the next good charater (next variable)
        endif
        i=0
        i2=0     ! nothing read now
        return
    endif     
     add=0
     add=index(s(i1:),sep)
     if(add .gt. 0)then
        i2=i1+add-2
     else
      ! end of line without separator
        i2=l
     endif
     read(s(i1:i2), '(i)',IOSTAT=ios,err=99) i    
! next good character (for the nex var) in i1
     i1=i2+2
     return
99   write(*,*)' error in parse_csv_int ',ios,' ',s(i1:i2),s(1:50)
     write(*,*)' i1 ',i1,' i2 ',i2,' len ',l
     pause
     stop
   end subroutine
   
 

   subroutine parse_csv_bigint(sep,ier,s,i1,i2,l,ii)
     integer, intent(IN) :: l
     integer, intent(OUT) :: ier
     integer, intent (INOUT) :: i1,i2
     character*(*), intent(IN) :: s
     character*1, intent(IN) :: sep
     integer*8, intent(OUT) :: ii
     integer add,ios
     
! suppose success
     ier=0
     ii=0    ! zero the output
     if(s(i1:i1) .eq. sep .or. s(i1:i1) .eq. '(' )then
! the variable is undefined (I find a separator in first position or
!                            the beginning of the "(NULL)" statement):
        if(s(i1:i1) .eq. sep )then 
          ier=1    ! declare undefined variable
          i1=i1+1  ! bump the next good charater
        else if(s(i1:i1+5) .eq. '(NULL)' .or.  s(i1:i1+5) .eq. '(null)')then
          ier=2    ! declare NULL variable
          i1=i1+7  ! bump the next good charater
        endif
        ii=0
        i2=0     ! nothing read now
        return
    endif     
     add=0
     add=index(s(i1:),sep)
     if(add .gt. 0)then
        i2=i1+add-2
     else
      ! end of line without separator
        i2=l
     endif
     read(s(i1:i2), '(i)',IOSTAT=ios,err=99) ii    
! next good character (for the nex var) in i1
     i1=i2+2
     return
99   write(*,*)' error in parse_csv_bigint ',ios,' ',s(i1:i2),s(1:50)
     write(*,*)' i1 ',i1,' i2 ',i2,' len ',l
     pause
     STOP
   end subroutine
   
   subroutine parse_csv_real(sep,ier,s,i1,i2,l,r)
     integer, intent(OUT) :: ier
     integer, intent (INOUT) :: i1,i2
     integer, intent(IN) :: l
     character*1, intent(IN) :: sep
     character*(*), intent(IN) :: s
     real, intent(OUT) :: r
     integer add,ios
     
! suppose success
     ier=0
     r=0.0   ! zero the output
     if(s(i1:i1) .eq. sep .or. s(i1:i1) .eq. '(' )then
! the variable is undefined (I find a separator in first position or
!                            the beginning of the "(NULL)" statement):
        if(s(i1:i1) .eq. sep )then 
          ier=1    ! declare undefined variable
          i1=i1+1  ! bump the next good charater
        else if(s(i1:i1+5) .eq. '(NULL)' .or.  s(i1:i1+5) .eq. '(null)')then
          ier=2    ! declare NULL variable
          i1=i1+7  ! bump the next good charater
        endif
        r=0.0
        i2=0     ! nothing read now
        return
    endif     
     add=0
     add=index(s(i1:),sep)
     if(add .gt. 0)then
        i2=i1+add-2
     else
      ! end of line without separator
        i2=l
     endif
     read(s(i1:i2), '(f)',IOSTAT=ios,err=99) r    
! next good character (for the nex var) in i1
     i1=i2+2
     return
99   write(*,*)' error in parse_csv_real ',ios,' ',s(i1:i2),s(1:50)
     write(*,*)' i1 ',i1,' i2 ',i2,' len ',l
     pause
     STOP
   end subroutine    
   
   subroutine parse_csv_double(sep,ier,s,i1,i2,l,rr)
     integer, intent(OUT) :: ier
     integer, intent (INOUT) :: i1,i2
     integer, intent(IN) :: l
     character*1, intent(IN) :: sep
     character*(*), intent(IN) :: s
     real*8, intent(OUT) :: rr
     integer add,ios
     
! suppose success
     ier=0
     rr=0.0d00      ! zero the output
     if(s(i1:i1) .eq. sep .or. s(i1:i1) .eq. '(' )then
! the variable is undefined (I find a separator in first position or
!                            the beginning of the "(NULL)" statement):
        if(s(i1:i1) .eq. sep )then 
          ier=1    ! declare undefined variable
          i1=i1+1  ! bump the next good charater
        else if(s(i1:i1+5) .eq. '(NULL)' .or.  s(i1:i1+5) .eq. '(null)')then
          ier=2    ! declare NULL variable
          i1=i1+7  ! bump the next good charater
        endif
        rr=0.0d00
        i2=0     ! nothing read now
        return
    endif     
     add=0
     add=index(s(i1:),sep)
     if(add .gt. 0)then
        i2=i1+add-2
     else
      ! end of line without separator
        i2=l
     endif
     read(s(i1:i2), '(d)',IOSTAT=ios,err=99) rr    
! next good character (for the nex var) in i1
     i1=i2+2
     return
99   write(*,*)' error in parse_csv_double ',ios,' ',s(i1:i2),s(1:50)
     write(*,*)' i1 ',i1,' i2 ',i2,' len ',l
     pause
     STOP
   end subroutine   



   subroutine parse_csv_char(sep,ier,s,i1,i2,l,cc,n)
     integer, intent(OUT) :: ier
     integer, intent (INOUT) :: i1,i2
     integer, intent(IN) :: l
     character*1, intent(IN) :: sep
     character*(*), intent(IN) :: s
     character*(*), intent(OUT) :: cc
     integer, intent(OUT) :: n
     integer add,ios
     
! suppose success
     ier=0
     cc=' '    ! zero the output
     n=0
     if(s(i1:i1) .eq. sep .or. s(i1:i1) .eq. '(' )then
! the variable is undefined (I find a separator in first position or
!                            the beginning of the "(NULL)" statement):
        if(s(i1:i1) .eq. sep )then 
          ier=1    ! declare undefined variable
          i1=i1+1  ! bump the next good charater
        else if(s(i1:i1+5) .eq. '(NULL)' .or.  s(i1:i1+5) .eq. '(null)')then
          ier=2    ! declare NULL variable
          i1=i1+7  ! bump the next good charater
        endif
        cc=' '
        i2=0     ! nothing read now
        return
    endif     
     add=0
     add=index(s(i1:),sep)
     if(add .gt. 0)then
        i2=i1+add-2
     else
      ! end of line without separator
        i2=l
     endif
     n=i2-i1+1    !number of characters
     i=' '
     read(s(i1:i2), '(a<n>)',IOSTAT=ios,err=99) cc    
! next good character (for the nex var) in i1
     i1=i2+2
     return
99   write(*,*)' error in parse_csv_char ',ios,' ',s(i1:i2),s(1:50)
     write(*,*)' i1 ',i1,' i2 ',i2,' len ',l
     pause
     STOP
   end subroutine
END MODULE 

